#include "qemu/osdep.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "sysemu/runstate.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/qemu-print.h"
#include "crypto/random.h"
#include "qapi/error.h"

#define EFUSE_REGION_SIZE 0x10000

#define TYPE_EFUSE "efuse"
#define EFUSE(obj) \
    OBJECT_CHECK(efuse_state, (obj), TYPE_EFUSE)

/* DJTAG中断源寄存器。 */
#define SC_DJTAG_SRC_INT		0x4008
/* DJTAG中断屏蔽寄存器。 */
#define SC_DJTAG_INT_MASK		0x400C
/* DJTAG中断状态寄存器。 */
#define SC_DJTAG_INT_STATUS		0x8008
/* DJTAG访问各模块内安全世界寄存器的安全访问控制使能寄存器。 */
#define SC_DJTAG_SEC_ACC_EN		0xD800
/* DJTAG快速模式选择寄存器。 */
#define SC_DJTAG_FAST_MODE		0xD804
/* SC_DJTAG_MSTR_ADDR为DJTAG Master配置访问模块的偏移地址寄存器。 */
#define SC_DJTAG_MSTR_ADDR		0xD810
/* SC_DJTAG_MSTR_DATA为DJTAG Master的写数据寄存器。 */
#define SC_DJTAG_MSTR_DATA		0xD814
/* SC_DJTAG_MSTR_CFG为DJTAG Master的配置寄存器。 */
#define SC_DJTAG_MSTR_CFG		0xD818
/* SC_DJTAG_MSTR_START_EN为DJTAG Master访问chain链的start使能寄存器。 */
#define SC_DJTAG_MSTR_START_EN		0xD81C
/* DJTAG_MSTR PIPELINE配置寄存器。 */
#define SC_DJTAG_MSTR_PIPE_CFG		0xD820
/* SC_DJTAG_TMOUT为IDIE中DJTAG Master的状态机超时阈值寄存器。 */
#define SC_DJTAG_TMOUT		0xD840
/* DJTAG安全访问模式寄存器。 */
#define SC_DJTAG_SEC_MODE		0xD844
/* DJTAG_MSTR鉴权访问寄存器。 */
#define SC_DJTAG_AUTH_EN		0xE030
/* DJTAG扫描链上第一个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA0		0xE800
/* DJTAG扫描链上第二个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA1		0xE804
/* DJTAG扫描链上第三个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA2		0xE808
/* DJTAG扫描链上第四个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA3		0xE80C
/* DJTAG扫描链上第五个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA4		0xE810
/* DJTAG扫描链上第六个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA5		0xE814
/* DJTAG扫描链上第七个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA6		0xE818
/* DJTAG扫描链上第八个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA7		0xE81C
/* DJTAG扫描链上第九个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA8		0xE820
/* DJTAG扫描链上第十个模块读数据寄存器。 */
#define SC_DJTAG_RD_DATA9		0xE824
/* DJTAG访问状态寄存器。 */
#define SC_DJTAG_OP_ST		0xE828
/* FPGA版本寄存器。 */
#define FPGA_VER		0xFFFC

typedef struct {
    uint32_t sc_djtag_src_int;
    uint32_t sc_djtag_int_mask;
    uint32_t sc_djtag_int_status;
    uint32_t sc_djtag_sec_acc_en;
    uint32_t sc_djtag_fast_mode;
    uint32_t sc_djtag_mstr_addr;
    uint32_t sc_djtag_mstr_data;
    uint32_t sc_djtag_mstr_cfg;
    uint32_t sc_djtag_mstr_start_en;
    uint32_t sc_djtag_mstr_pipe_cfg;
    uint32_t sc_djtag_tmout;
    uint32_t sc_djtag_sec_mode;
    uint32_t sc_djtag_auth_en;
    uint32_t sc_djtag_rd_data0;
    uint32_t sc_djtag_rd_data1;
    uint32_t sc_djtag_rd_data2;
    uint32_t sc_djtag_rd_data3;
    uint32_t sc_djtag_rd_data4;
    uint32_t sc_djtag_rd_data5;
    uint32_t sc_djtag_rd_data6;
    uint32_t sc_djtag_rd_data7;
    uint32_t sc_djtag_rd_data8;
    uint32_t sc_djtag_rd_data9;
    uint32_t sc_djtag_op_st;
    uint32_t fpga_ver;
} efuse_regs;

typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq     irq;

    efuse_regs regs;
} efuse_state;

static const VMStateDescription vmstate_efuse = {
    .name = TYPE_EFUSE,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_END_OF_LIST()
    }
};

static void efuse_reset(DeviceState *d)
{
}

static uint64_t efuse_read_part0(void *opaque, hwaddr offset, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case SC_DJTAG_SRC_INT:
        return regs->sc_djtag_src_int;
    case SC_DJTAG_INT_MASK:
        return regs->sc_djtag_int_mask;
    case SC_DJTAG_INT_STATUS:
        regs->sc_djtag_int_status = regs->sc_djtag_src_int & (regs->sc_djtag_int_mask ^ 0x1);
        return regs->sc_djtag_int_status;
    case SC_DJTAG_SEC_ACC_EN:
        return regs->sc_djtag_sec_acc_en;
    case SC_DJTAG_FAST_MODE:
        return regs->sc_djtag_fast_mode;
    case SC_DJTAG_MSTR_ADDR:
        return regs->sc_djtag_mstr_addr;
    case SC_DJTAG_MSTR_DATA:
        return regs->sc_djtag_mstr_data;
    case SC_DJTAG_MSTR_CFG:
        return regs->sc_djtag_mstr_cfg;
    case SC_DJTAG_MSTR_START_EN:
        return regs->sc_djtag_mstr_start_en;
    case SC_DJTAG_MSTR_PIPE_CFG:
        return regs->sc_djtag_mstr_pipe_cfg;
    case SC_DJTAG_TMOUT:
        return regs->sc_djtag_tmout;
    case SC_DJTAG_SEC_MODE:
        return regs->sc_djtag_sec_mode;
    default:
        break;
    }
    return 0;
}
static uint64_t efuse_read_part1(void *opaque, hwaddr offset, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case SC_DJTAG_AUTH_EN:
        return regs->sc_djtag_auth_en;
    case SC_DJTAG_RD_DATA0:
        return regs->sc_djtag_rd_data0;
    case SC_DJTAG_RD_DATA1:
        return regs->sc_djtag_rd_data1;
    case SC_DJTAG_RD_DATA2:
        return regs->sc_djtag_rd_data2;
    case SC_DJTAG_RD_DATA3:
        return regs->sc_djtag_rd_data3;
    case SC_DJTAG_RD_DATA4:
        return regs->sc_djtag_rd_data4;
    case SC_DJTAG_RD_DATA5:
        return regs->sc_djtag_rd_data5;
    case SC_DJTAG_RD_DATA6:
        return regs->sc_djtag_rd_data6;
    case SC_DJTAG_RD_DATA7:
        return regs->sc_djtag_rd_data7;
    case SC_DJTAG_RD_DATA8:
        return regs->sc_djtag_rd_data8;
    case SC_DJTAG_RD_DATA9:
        return regs->sc_djtag_rd_data9;
    case SC_DJTAG_OP_ST:
        return regs->sc_djtag_op_st;
    default:
        break;
    }
    return 0;
}
static uint64_t efuse_read_part2(void *opaque, hwaddr offset, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case FPGA_VER:
        return regs->fpga_ver;
    default:
        break;
    }
    return 0;
}

static uint64_t efuse_read(void *opaque, hwaddr offset, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    uint64_t ret = 0;
    if ((offset >= SC_DJTAG_SRC_INT) && (offset <= SC_DJTAG_SEC_MODE)) {
        ret =  efuse_read_part0(opaque, offset, size);
    }  else if ((offset >= SC_DJTAG_AUTH_EN) && (offset <= SC_DJTAG_OP_ST)) {
        ret =  efuse_read_part1(opaque, offset, size);
    }  else if ((offset >= FPGA_VER) && (offset <= FPGA_VER)) {
        ret =  efuse_read_part2(opaque, offset, size);
    }  else {
        ret = -1;
    }
    return ret;
}

static void efuse_write_part0(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case SC_DJTAG_SRC_INT:
        regs->sc_djtag_src_int = 0x0;
        break;
    case SC_DJTAG_INT_MASK:
        regs->sc_djtag_int_mask = val & 0x1;
        break;
    case SC_DJTAG_INT_STATUS:
        break;
    case SC_DJTAG_SEC_ACC_EN:
        if (regs->sc_djtag_mstr_start_en & 0x1) {
            regs->sc_djtag_sec_acc_en = (val&0x1);
        }
        break;
    case SC_DJTAG_FAST_MODE:
        break;
    case SC_DJTAG_MSTR_ADDR:
        if (regs->sc_djtag_mstr_start_en & 0x1) {
            regs->sc_djtag_mstr_addr = (val & 0x7fffffff);
        }
        break;
    case SC_DJTAG_MSTR_DATA:
        if (regs->sc_djtag_mstr_start_en & 0x1) {
            regs->sc_djtag_mstr_data = val;
        }
        break;
    case SC_DJTAG_MSTR_CFG:
        regs->sc_djtag_mstr_cfg = (val & 0x80000000);
        if (regs->sc_djtag_mstr_start_en & 0x1) {
            regs->sc_djtag_mstr_cfg |= (val & 0x2ffffff);
        }
        break;
    case SC_DJTAG_MSTR_START_EN:
        regs->sc_djtag_mstr_start_en = (val & 0x1);
        if (val & 0x1) {
            regs->sc_djtag_op_st |= 0x100;
        }
        break;
    default:
        break;
    }
    return;
}

static void efuse_write_part1(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case SC_DJTAG_MSTR_PIPE_CFG:
        regs->sc_djtag_mstr_pipe_cfg = (val & 0x17f);
        break;
    case SC_DJTAG_TMOUT:
        regs->sc_djtag_tmout = val;
        break;
    case SC_DJTAG_SEC_MODE:
        if (((regs->sc_djtag_tmout & 0x1) == 1) && ((val & 0x1) == 0)) {
            regs->sc_djtag_fast_mode = 0;
            regs->sc_djtag_mstr_addr = 0;
            regs->sc_djtag_mstr_data = 0;
            regs->sc_djtag_mstr_cfg = 0;
            regs->sc_djtag_mstr_pipe_cfg = 0;
            regs->sc_djtag_tmout = 0;
            regs->sc_djtag_rd_data0 = 0;
            regs->sc_djtag_rd_data1 = 0;
            regs->sc_djtag_rd_data2 = 0;
            regs->sc_djtag_rd_data3 = 0;
            regs->sc_djtag_rd_data4 = 0;
            regs->sc_djtag_rd_data5 = 0;
            regs->sc_djtag_rd_data6 = 0;
            regs->sc_djtag_rd_data7 = 0;
            regs->sc_djtag_rd_data8 = 0;
            regs->sc_djtag_rd_data9 = 0;
            regs->sc_djtag_op_st = 0;
        }
        regs->sc_djtag_sec_mode = (val & 0x1);
        break;
    case SC_DJTAG_AUTH_EN:
        break;
    default:
        break;
    }
    return;
}
static void efuse_write_part2(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    switch (offset) {
    case SC_DJTAG_RD_DATA0:
        break;
    case SC_DJTAG_RD_DATA1:
        break;
    case SC_DJTAG_RD_DATA2:
        break;
    case SC_DJTAG_RD_DATA3:
        break;
    case SC_DJTAG_RD_DATA4:
        break;
    case SC_DJTAG_RD_DATA5:
        break;
    case SC_DJTAG_RD_DATA6:
        break;
    case SC_DJTAG_RD_DATA7:
        break;
    case SC_DJTAG_RD_DATA8:
        break;
    case SC_DJTAG_RD_DATA9:
        break;
    case SC_DJTAG_OP_ST:
        break;
    case FPGA_VER:
        break;
    default:
        break;
    }
    return;
}

static void efuse_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    efuse_state *s = (efuse_state *)opaque;
    efuse_regs *regs = &s->regs;
    if ((offset >= SC_DJTAG_SRC_INT) && (offset <= SC_DJTAG_MSTR_START_EN)) {
        efuse_write_part0(opaque, offset, val, size);
    } else if ((offset >= SC_DJTAG_MSTR_PIPE_CFG) && (offset <= SC_DJTAG_AUTH_EN)) {
        efuse_write_part1(opaque, offset, val, size);
    }  else if ((offset >= SC_DJTAG_RD_DATA0) && (offset <= FPGA_VER)) {
        efuse_write_part2(opaque, offset, val, size);
    }
}


static const MemoryRegionOps efuse_ops = {
    .read = efuse_read,
    .write = efuse_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.min_access_size = 4,
    .valid.max_access_size = 8,
    .impl.max_access_size = 4,
};

static void efuse_init(Object *obj)
{
    DeviceState *dev = DEVICE(obj);
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    efuse_state *s = EFUSE(obj);
    efuse_regs *regs = &(s->regs);
    memset(regs, 0, sizeof(efuse_regs));
    regs->sc_djtag_int_mask = 0x01;
    regs->sc_djtag_mstr_cfg = 0xffff;
    regs->sc_djtag_tmout = 0x017D7840;
    regs->sc_djtag_sec_mode = 0x01;
    regs->sc_djtag_auth_en = 0x11;
    regs->sc_djtag_op_st = 0x1000000000;
    memory_region_init_io(&s->iomem, obj, &efuse_ops, s, TYPE_EFUSE, EFUSE_REGION_SIZE);
    sysbus_init_mmio(sd, &s->iomem);
    sysbus_init_irq(sd, &s->irq);
}

static void efuse_realize(DeviceState *d, Error **errp)
{
}

static void efuse_finalize(Object *obj)
{
}

static Property efuse_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static void efuse_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    dc->realize = efuse_realize;
    dc->reset = efuse_reset;
    dc->vmsd = &vmstate_efuse;
    device_class_set_props(dc, efuse_properties);
}

static const TypeInfo efuse_info = {
    .name          = TYPE_EFUSE,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(efuse_state),
    .instance_init = efuse_init,
    .instance_finalize = efuse_finalize,
    .class_init    = efuse_class_init,
};

static void efuse_register_types(void)
{
    type_register_static(&efuse_info);
}

type_init(efuse_register_types)
